package cn.fanqinghong.ymcc.service.impl;

import cn.fanqinghong.ymcc.constants.CourseConstants;
import cn.fanqinghong.ymcc.constants.CourseError;
import cn.fanqinghong.ymcc.doc.CourseDoc;
import cn.fanqinghong.ymcc.domain.*;
import cn.fanqinghong.ymcc.dto.*;
import cn.fanqinghong.ymcc.feignClient.UserFeignClient;
import cn.fanqinghong.ymcc.feignClients.CourseSearchFeignClient;
import cn.fanqinghong.ymcc.feignclient.MediaFileFeignClient;
import cn.fanqinghong.ymcc.mapper.CourseMapper;
import cn.fanqinghong.ymcc.mq.producer.MessageHandlerProducer;
import cn.fanqinghong.ymcc.result.JSONResult;
import cn.fanqinghong.ymcc.service.*;
import cn.fanqinghong.ymcc.util.AssertUtil;
import cn.fanqinghong.ymcc.util.SmsUtils;
import cn.fanqinghong.ymcc.util.StrUtils;
import cn.fanqinghong.ymcc.vo.CourseItemVo;
import cn.fanqinghong.ymcc.vo.CourseOrderVo;
import cn.fanqinghong.ymcc.vo.SearchCourseVo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author superman.fan
 * @since 2022-07-20
 */
@Slf4j
@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements ICourseService {

    @Autowired
    private ITeacherService teacherService;
    @Autowired
    private ICourseDetailService courseDetailService;
    @Autowired
    private ICourseMarketService courseMarketService;
    @Autowired
    private ICourseResourceService courseResourceService;
    @Autowired
    private ICourseSummaryService courseSummaryService;
    @Autowired
    private ICourseTypeService courseTypeService;
    @Autowired
    private ICourseTeacherService courseTeacherService;
    @Autowired
    private CourseSearchFeignClient courseSearchFeignClient;
    @Autowired
    private UserFeignClient userFeignClient;
    @Autowired
    private MessageHandlerProducer messageHandlerProducer;
    @Autowired
    private MediaFileFeignClient mediaFileFeignClient;

    @Autowired
    private ICourseChapterService courseChapterService;


    /**
     * 查询订单结算页的数据
     * @param courseIds
     * @return
     */
    @Override
    public CourseOrderVo courseOrderInfo(String courseIds) {
        //1.将传入的String的课程id截取成Long类型的数组
        List<Long> ids = StrUtils.splitStr2LongArr(courseIds);
        //去重复
        Set<Long> setIds = ids.stream().collect(Collectors.toSet());
        //判断是否重复
        AssertUtil.isTrue(ids.size() == setIds.size(),"请求非法");
        ArrayList<CourseItemVo> courseInfos = new ArrayList<>();
        BigDecimal totalAmount = new BigDecimal(0);
        //2.遍历去重后的课程id，获取课程信息和总价
        totalAmount = getCourseAndTotalAmount(setIds, courseInfos, totalAmount);
        //4.返回总价
        return new CourseOrderVo(courseInfos,totalAmount);
    }

    /**
     * 遍历去重后的课程id，获取课程信息和总价
     * @param setIds
     * @param courseInfos
     * @param totalAmount
     * @return
     */
    private BigDecimal getCourseAndTotalAmount(Set<Long> setIds, ArrayList<CourseItemVo> courseInfos, BigDecimal totalAmount) {
        for (Long id : setIds) {
            //3.获取课程，课程收费信息
            Course course = selectById(id);
            AssertUtil.isTrue(course.getStatus()==Course.STATE_ON,"请求非法");
            AssertUtil.isNotNull(course,"请求非法");
            CourseMarket courseMarket = courseMarketService.selectById(id);
            CourseItemVo courseItemVo = new CourseItemVo(course,courseMarket);
            //将课程信息包装到List数组中
            courseInfos.add(courseItemVo);
            //计算总价
            totalAmount = totalAmount.add(courseMarket.getPrice());

        }
        return totalAmount;
    }


    /**
     * 查询课程的所有信息
     * @param courseId
     * @return
     */
    @Override
    public SearchCourseVo searchCourseDetail(Long courseId) {
        AssertUtil.isNotNull(courseId,CourseConstants.REQUEST_IS_ILLEGALITY);
        //1.获取课程
        Course course = selectById(courseId);
        AssertUtil.isTrue(course.getStatus() == Course.STATE_ON,CourseConstants.REQUEST_IS_ILLEGALITY);
        AssertUtil.isNotNull(course,CourseConstants.REQUEST_IS_ILLEGALITY);
        //2.通过courseId查询中间表获得课程的老师信息
        List<Teacher> teacherInfo = getTeacherInfo(courseId);
        //3.获得章节详情
        List<CourseChapter> courseChapters = getCourseChapters(courseId);
        //4.获得课程的详情
        CourseDetail courseDetail = courseDetailService.selectById(courseId);
        //5.获取课程的评论信息
        CourseSummary courseSummary = courseSummaryService.selectById(courseId);
        //6.获取课程的销售信息
        CourseMarket courseMarket = courseMarketService.selectById(courseId);

        //7.设置课程的所有信息
        SearchCourseVo searchCourseVo = setSearchCourseVo(course, teacherInfo, courseChapters, courseDetail, courseSummary, courseMarket);


        return searchCourseVo;
    }



    /**
     * 获取章节详情，并且将视频放入章节
     * @param courseId
     * @return
     */
    private List<CourseChapter> getCourseChapters(Long courseId) {
        Wrapper<CourseChapter> courseChapterWrapper = new EntityWrapper<>();
        courseChapterWrapper.eq("course_id", courseId);
        //获取所有章节，封装到list
        List<CourseChapter> courseChapters = courseChapterService.selectList(courseChapterWrapper);
        //遍历章节信息设置视频信息到list中
        courseChapters.forEach(courseChapter -> {
            Long chapterId = courseChapter.getId();
            Object data = mediaFileFeignClient.searchInfo(chapterId).getData();
            List<MediaFile> mediaFiles = JSONObject.parseArray(JSON.toJSON(data).toString(), MediaFile.class);
            //设置url为null，在单独写一个接口去查询url的数据
            mediaFiles.stream().forEach(mediaFile -> {
                mediaFile.setFileUrl(null);
            });
            courseChapter.setMediaFiles(mediaFiles);
        });
        return courseChapters;
    }

    /**
     * 设置返回的课程所有的信息
     * @param course 课程
     * @param teacherInfo   老师信息
     * @param courseChapters    章节信息
     * @param courseDetail  课程详情
     * @param courseSummary 课程评论，点击量
     * @param courseMarket     课程收费，咨询
     * @return
     */
    private SearchCourseVo setSearchCourseVo(Course course, List<Teacher> teacherInfo, List<CourseChapter> courseChapters, CourseDetail courseDetail, CourseSummary courseSummary, CourseMarket courseMarket) {
        SearchCourseVo searchCourseVo = new SearchCourseVo();
        searchCourseVo.setCourse(course);
        searchCourseVo.setCourseChapters(courseChapters);
        searchCourseVo.setCourseDetail(courseDetail);
        searchCourseVo.setCourseMarket(courseMarket);
        searchCourseVo.setCourseSummary(courseSummary);
        searchCourseVo.setTeachers(teacherInfo);
        return searchCourseVo;
    }

    /**
     * 通过courseId查询中间表获得课程的老师信息
     * @param courseId
     * @return
     */
    private List<Teacher> getTeacherInfo(Long courseId) {
        Wrapper<CourseTeacher> teacherWrapper = new EntityWrapper<>();
        teacherWrapper.eq("course_id", courseId);
        List<CourseTeacher> courseTeachers = courseTeacherService.selectList(teacherWrapper);
        //得到当前课程的所有老师id
        List<Long> teacherIds = courseTeachers.stream().map(teacher -> teacher.getTeacherId()).collect(Collectors.toList());
        //使用查询到的中间表的老师id查询老师信息
        return teacherService.selectBatchIds(teacherIds);
    }

    /**
     *  保存课程信息
     * @param dto
     */
    @Override
    @Transactional
    public void save(CourseDto dto) {
        Course course = dto.getCourse();
        CourseDetail courseDetail = dto.getCourseDetail();
        CourseMarket courseMarket = dto.getCourseMarket();
        CourseResource courseResource = dto.getCourseResource();
        List<Long> teacharIds = dto.getTeacharIds();
        //1.参数校验 jsr303
        //2.课程不能被注册
        Course courseTmp = selectByName(course.getName());
        AssertUtil.isNull(courseTmp, CourseError.COURSE_IS_REGISTER.getMessage());
        //3.保存数据
        //3.1 保存课程
        saveCourse(course, teacharIds);
        //3.2保存课程详情
        saveCourseDetail(course, courseDetail);
        //3.3 保存课程销量
        saveCourseMarket(course, courseMarket);
        //3.4 保存课程课程讲师
        saveCourseTeacher(course, teacharIds);
        //3.5 保存课程资源
        saveCourseResource(course, courseResource);
        //初始化课程统计
        saveCourseSummary(course);
        //修改课程分类的数量+1
        addCourseTypeCount(course);

    }

    /**
     * 课程上架
     * 1.根据课程id查询数据，校验课程是否存在
     * 2.修改课程状态为上架，填写上架时间
     * 3.保存课程到es
     *  3.1 为service—search编写feign接口
     *  3.2在课程微服务，依赖ymcc-api-search
     *  3.3使用feign接口远程调用search完成课程保存到es中
     * @param courseIds
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void onLineCourse(List<Long> courseIds) {

        for(Long courseId:courseIds) {
            //1.查根据传入的课程id查询数据
            Course course = selectById(courseId);
            AssertUtil.isNotNull(course, CourseConstants.COURSE_INTALL_ERROR);
            boolean offLine = course.getStatus() == Course.STATE_OFF;
            AssertUtil.isTrue(offLine, CourseConstants.COURSE_IS_ON);
            //2.修改课程状态为上架
            course.setStatus(Course.STATE_ON);
            //修改上架时间
            course.setOnlineTime(new Date());
            updateById(course);
            //3.保存课程到es
            saveCourseToES(courseId, course);
            //查询user的数据
            List<UserDto> userList = getUserList();
            //生产者发送消息
            sendProducer(courseId, course, userList);


        }

    }

    /**
     * 保存课程到es
     * @param courseId
     * @param course
     */
    private void saveCourseToES(Long courseId, Course course) {
        CourseDoc doc = new CourseDoc();
        //根据课程id查询课程评论的信息
        CourseSummary courseSummary = courseSummaryService.selectById(courseId);
        //查询课程收费信息
        CourseMarket courseMarket = courseMarketService.selectById(courseId);
        //查询课程的类型
        CourseType courseType = courseTypeService.selectById(course.getCourseTypeId());
        //将CourseSummary中的数据同步到CourseDOc中
        BeanUtils.copyProperties(courseSummary, doc);
        //将course中的数据同步到CourseDOc中
        BeanUtils.copyProperties(course, doc);
        //将courseMarket中的数据同步到CourseDOc中
        BeanUtils.copyProperties(courseMarket, doc);
        //单独设置是否免费
        doc.setChargeName(courseMarket.getCharge() == 1 ? "免费" : "收费");
        //将courseType中的数据同步到CourseDOc中
        doc.setCourseTypeName(courseType.getName());
        //调用feign接口保存课程信息到ES中
        JSONResult jsonResult = courseSearchFeignClient.saveCourse(doc);
        AssertUtil.isTrue(jsonResult.isSuccess(), CourseConstants.COURSE_ISSUE_DEFEAD);
    }

    /**
     * 生产者发送消息
     * @param courseId 课程id
     * @param course    课程内容
     * @param userList  用户信息
     */
    private void sendProducer(Long courseId, Course course, List<UserDto> userList) {
        //设置需要发送的信息
        StationMessageDto stationMessageDto = saveMessageStation(userList, courseId, course);
        //发送站内信到mq
        messageHandlerProducer.sendStationMeassage(stationMessageDto);
        SmsMessageDto smsMessageDto = setMessageSms(userList);
        //发送短信到mq
        messageHandlerProducer.sendSMSMessage(smsMessageDto);
        EmailMessageDto emailMessageDto = setMessageEmail(userList);
        //发送邮件到mq
        messageHandlerProducer.sendMessageEmail(emailMessageDto);
    }

    /**
     * 传递给生产者的短信内容
     * @param
     * @return
     */
    private SmsMessageDto setMessageSms(List<UserDto> dtos) {
        SmsMessageDto smsMessageDto = new SmsMessageDto();
        smsMessageDto.setContent("亲爱的用户您好，最新课程 <a href='http://course.ymcc.com:6002/detail.html?courseId=\"+ courseId +\"'>\"+ course.getName()+\"</a> 发布啦，点击课程了解详情");
        smsMessageDto.setTitle("课程发布");
        smsMessageDto.setDtos(dtos);
        return smsMessageDto;
    }

    /**
     * 查询user的数据，将其包装到list中
     * @return
     */
    private List<UserDto> getUserList(){
        //调用feign接口查询用户的信息
        Object userList = userFeignClient.list().getData();
        List<User> users = JSON.parseArray(JSON.toJSON(userList).toString(), User.class);
        ArrayList<UserDto> dtos = new ArrayList<>();
        users.forEach(user -> {
            log.info(""+user);
            UserDto userDto = new UserDto();
            userDto.setId(user.getId());
            userDto.setPhone(user.getPhone());
            userDto.setEmali(user.getEmail());
            dtos.add(userDto);
        });
        return dtos;

    }

    /**
     * 传递发送邮件的数据
     * @param dtos
     * @return
     */
    private EmailMessageDto setMessageEmail(List<UserDto> dtos) {
        EmailMessageDto emailMessageDto = new EmailMessageDto();
        emailMessageDto.setContent("亲爱的用户您好，最新课程 <a href='测试'> 测试 </a> 发布啦，点击课程了解详情");
        emailMessageDto.setTitle("课程发布");
        emailMessageDto.setDtos(dtos);
        return emailMessageDto;
    }


    /**
     * 设置需要发送的信息
     * @param dtos
     * @param course
     * @return
     */
    private StationMessageDto saveMessageStation(List<UserDto> dtos,Long courseId, Course course) {
        StationMessageDto stationMessageDto = new StationMessageDto();
        stationMessageDto.setContent("亲爱的用户您好，最新课程 <a href='http://course.ymcc.com:6002/detail.html?courseId="+ courseId +"'>"+ course.getName()+"</a> 发布啦，点击课程了解详情");
        stationMessageDto.setTitle("课程发布啦");
        stationMessageDto.setType("系统消息");
        stationMessageDto.setDtos(dtos);
        return stationMessageDto;
    }

    /**
     * 发站内信
     * @param messageStation
     * @return
     */
    public JSONResult sendStationMessage(MessageStation messageStation){
        SmsUtils.sendSms(null,messageStation.getContent());
        return JSONResult.success();
    }

    /**
     * 视频下架
     * 1.根据courseId查询数据
     * //2.修改课程状态为下架
     * @param courseIds
     */
    @Override
    @GlobalTransactional
    public void offLineCourse(List<Long> courseIds) {
        for(Long courseId:courseIds) {
            //1.查根据传入的课程id查询数据
            Course course = selectById(courseId);
            AssertUtil.isNotNull(course, "课程上传错误");
            boolean offLine = course.getStatus() == Course.STATE_ON;
            AssertUtil.isTrue(offLine, "课程已下线");
            //2.修改课程状态为下架
            course.setStatus(Course.STATE_OFF);
            //修改下架时间
            course.setEndTime(new Date());
            updateById(course);
            CourseDoc courseDoc = new CourseDoc();
            courseDoc.setId(courseId);
            JSONResult jsonResult = courseSearchFeignClient.deleteCourse(courseDoc);
            AssertUtil.isTrue(jsonResult.isSuccess(), "课程发布失败");
        }

    }




    /**
     * 修改课程分类的数量+1
     * @param course
     */
    private void addCourseTypeCount(Course course) {
        CourseType  courseType = courseTypeService.selectById(course.getCourseTypeId());
        courseType.setTotalCount(courseType.getTotalCount()+1);
        courseTypeService.updateById(courseType);
    }

    /**
     * 初始化课程统计
     * @param course
     */
    @GlobalTransactional(rollbackFor = Exception.class)//全局异常处理
    private void saveCourseSummary(Course course) {
        CourseSummary courseSummary = new CourseSummary();
        courseSummary.setId(course.getId());
        courseSummaryService.insert(courseSummary);
    }

    /**
     * 3.5 保存课程资源
     * @param course
     * @param courseResource
     */
    private void saveCourseResource(Course course, CourseResource courseResource) {
        courseResource.setCourseId(course.getId());
        courseResourceService.insert(courseResource);
    }

    /**
     *  //3.4 保存课程课程讲师
     * @param course
     * @param teacharIds
     */
    private void saveCourseTeacher(Course course, List<Long> teacharIds) {
        teacharIds.forEach(id->{
            CourseTeacher courseTeacher = new CourseTeacher();
            courseTeacher.setCourseId(course.getId());
            courseTeacher.setTeacherId(id);
            courseTeacherService.insert(courseTeacher);
        });
    }

    /**
     * 3.3 保存课程销量
     * @param course
     * @param courseMarket
     */
    private void saveCourseMarket(Course course, CourseMarket courseMarket) {
        courseMarket.setId(course.getId());
        courseMarketService.insert(courseMarket);
    }

    /**
     * //3.2 保存课程详情
     * @param course
     * @param courseDetail
     */
    private void saveCourseDetail(Course course, CourseDetail courseDetail) {
        courseDetail.setId(course.getId());
        courseDetailService.insert(courseDetail);
    }

    /**
     *  //3.1 保存课程
     * @param course
     * @param teacharIds
     */
    private void saveCourse(Course course, List<Long> teacharIds) {
        //初始化资源  状态，讲师，谁来添加的课程
        //在service初始化值，防止被黑\
        course.setStatus(Course.STATE_OFF);
        // @TODO: 2022/7/21  后面做了登录以后来补充代码
        course.setLoginId(1l);
        course.setLoginUserName("yhptest1");
        //通过集合id查询数据
        List<Teacher> teachers = teacherService.selectBatchIds(teacharIds);
        //使用流的形式吧得到的老师名字填入list集合
        List<String> teacherNames = teachers.stream().map(teacher -> teacher.getName()).collect(Collectors.toList());
        //拼接成字符串
        String join = StringUtils.join(teacherNames, ",");
        course.setTeacherNames(join);
        insert(course);
    }

    /**
     * 通过name查询课程数据
     * @param name
     * @return
     */
    private Course selectByName(String name) {
        Wrapper<Course> wrapper = new EntityWrapper<>();
        wrapper.eq("name",name);
        return selectOne(wrapper);
    }
}
